# -- coding: utf-8 --
import os, time
from functools import wraps


# 异常处理装饰器
def excepter(place):
    def execute_func(func):  # 执行函数
        @wraps(func)  # 保存原有函数或方法的函数名和文档字符串
        def execute_except(*args, **kwargs):
            try:
                result = func(*args, **kwargs)
            except Exception as e:
                # 如果出现异常则记录在日志中
                LogHandler.recorder(place, str(e))
                result = None
            return result
        return execute_except
    return execute_func


class Base:

    # 该类中的方法都是对python基础方法和库的进一步封装

    @classmethod
    def get_time(cls, format):
        """
        按提供的日期格式返回对应的日期字符串
        :param format: %Y年 %m月 %d日  %H时 %M分 %S秒，区分大小写
        :return: 日期格式字符串
        """
        return time.strftime(format, time.localtime())

    @classmethod
    def create_dir(cls, path):
        """
        判断某个路径是否存在，如果不存在则创建它
        :param path: 要检查和创建的路径
        :return: None
        """
        if not os.path.exists(path):
            os.mkdir(path)


class FileParser:   # 文件解析器

    @classmethod
    def read_json(cls, path):
        """
        读取json格式文件，可以支持//或/**/格式的注释
        :param path:json文件的路径
        :return:python数据类型，如字典或列表
        """
        import json5
        with open(path, encoding='utf8') as file:
            contents = json5.load(file)
        return contents

    @classmethod
    def read_txt(cls, path, symbol):
        """
        按行读取某个文本文件，返回每一行有效内容的列表，支持注释
        :param path: 要读取的文本文件
        :param symbol: 注释的符号
        :return: 有效内容的字符串列表
        """
        with open(path, encoding='utf8') as file:
            contents = file.readlines()
        result = []
        for item in contents:
            temp = item.strip()
            if not temp.startswith(symbol) and temp!='':
                if symbol in temp:
                    rl = temp.split(symbol)[0].strip()
                    result.append(rl)
                else:
                    result.append(temp)
        return result

    @classmethod
    def read_conf(cls, path, section):
        import configparser   # 原生配置文件解析工具
        cp = configparser.ConfigParser()   # 生成解析器对象
        cp.read(path)   # 读取配置文件
        # value = cp.get(section, option)   # 通过section下的option获取其value
        # print(value)
        all = cp.items(section)     # 通过section获取其下的所有内容
        print(all)

    @classmethod
    def parse_ini_by_option(cls, path, section, option):
        """
        根据ini配置文件中的section和option获取option的值
        :param path: 配置文件的路径
        :param section: 节点
        :param option: 项的键名
        :return: 项的值名 注意是字符串
        """

        all = cls.parse_ini_by_section(path, section)
        value = None
        for item in all:
            value = item.get(option)
            if value is not None:
                return value
        return value

    @classmethod
    def parse_ini_by_section(cls, path, section):
        """
        根据ini配置文件中的section解析其内容，返回json格式内容
        :param path: 配置文件路径
        :param section: 节点名称
        :return: 列表嵌套字典。注意字典的值是字符串
        """
        import configparser
        cp = configparser.ConfigParser()  # 创建解析器对象
        cp.read(path)  # 将文件内容读取到cp中
        all = cp.items(section)
        result = []
        for item in all:
            di = {}
            di[item[0]] = item[1]
            result.append(di)
        return result

    @classmethod
    def read_excel(cls, path, sheet_name):
        """
        从excel文件中读取sheet页的内容
        :param path: excel文件路径
        :param sheet_name: 要读取的这个excel文件的sheet页的名字
        :return: sheet页对象
        """
        import xlrd
        workbook = xlrd.open_workbook(path)   # 工作蒲
        return workbook.sheet_by_name(sheet_name)



    @classmethod
    def parse_excel(cls, conf_path, section, option):
        """
        通过读取配置信息获取excel中测试数据信息
        :param conf_path: 配置文件路径
        :param section: 节点。这里应该是页面名称
        :param option: 项名。这里应该是功能名称
        :return: json格式内容
        """
        # 用例信息case_info
        case_info = eval(cls.parse_ini_by_option(conf_path, section, option))
        sheet = cls.read_excel(case_info['path'], case_info['sheet_name'])
        cases_info = []
        for i in range(case_info['start_row'], case_info['end_row']):
            info = {}
            info['caseid'] = sheet.cell(i, 0).value
            info['function'] = sheet.cell(i, 1).value
            info['type'] = sheet.cell(i, 2).value
            info['desc'] = sheet.cell(i, 3).value
            info['data'] = eval(sheet.cell(i, 4).value)
            info['expect'] = sheet.cell(i, 5).value
            cases_info.append(info)
        return cases_info


class DBParser:    # 数据库解析器
    @classmethod
    def get_conn(cls, dbname):   # 先检查base.json文件中的键
        """
        通过指定的配置文件获得数据库连接对象
        :param dbname:数据库名称
        :return:数据库连接对象
        """
        import pymysql
        db_info = FileParser.read_json('../conf/base.json')
        return pymysql.connect(**db_info[dbname])

    @classmethod
    def update(cls, dbname, sql):
        """
        传入一条DML语句，执行该语句
        :param sql: DML语句，即增删改操作
        :return: None
        """
        conn = cls.get_conn(dbname)
        cursor = conn.cursor()
        cursor.execute(sql)
        conn.commit()
        cursor.close()
        conn.close()

    @classmethod
    def query(cls, dbname, sql):
        """
        传入一条DQL语句，执行该语句并返回查询结果
        :param sql: SQL语句，即查询操作
        :return: 查询结果集，是二维元组
        """
        conn = cls.get_conn(dbname)
        cursor = conn.cursor()
        cursor.execute(sql)
        result = cursor.fetchall()
        cursor.close()
        conn.close()
        return result


class LogHandler:   # 日志处理器
    # 思路：
    # 1.在logs下创建一个文本文件，文本文件的文件名格式为：%Y/%m/%d %H:%M:%S.log
    # 2.明确要写入日志中的内容和格式：首先一个日志文件由多行记录组成，一条记录应该由：时间+地点（模块.类.方法）+内容组成；
    # 3.内容应该传参，可能是堆栈报错信息，也可能是自己想写的内容；
    # 4.以追加方式写入。a+

    filename = None

    @classmethod
    def get_filename(cls):
        """
        获取日志文件的文件名及其路径
        :return: 路径和文件名
        """
        Base.create_dir('..\logs')
        ftime = Base.get_time('%Y-%m-%d %H-%M-%S')
        return f'logs/{ftime}.log'

    @classmethod
    def get_log_record(cls, place, content):
        """
        获取日志文件内记录的格式和内容
        :param place: 地点，规范为：模块名.类名.方法名，即可知道在哪个模块下的哪个类下的哪个方法
        :param content: 记录了什么内容
        :return: 格式化后的记录
        """
        ftime = Base.get_time('%Y/%m/%d %H:%M:%S')
        return f"{ftime}---------------{place}----------------{content}\n"

    @classmethod
    def recorder(cls, place, content):
        """
        在任意位置调用该方法，记录相应的内容
        :param place:
        :param content:
        :return:
        """
        if cls.filename is None:
            cls.filename = cls.get_filename()
        record = cls.get_log_record(place, content)
        with open(cls.filename, 'a+') as file:
            file.write(record)


if __name__ == '__main__':

    # FileParser.parse_excel('../data/ai.xls', 'pr', 1, 4)
    # FileParser.read_conf('../conf/base.ini', 'ui')
    # print(FileParser.parse_ini_by_option('../conf/base.ini', 'login', 'cr'))
    print(FileParser.parse_excel('../conf/case.ini', 'pr', 'add_assets'))